#ifndef D4F97547_84E3_4137_B95D_E2179C0CD3ED
#define D4F97547_84E3_4137_B95D_E2179C0CD3ED

#include <iostream>
#include <arpa/inet.h>
#include <string>
#include <functional>
using namespace std;
#include <unordered_map>
#include <memory>
#include "Log.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"

//声明类
class Connection;
class TcpServer;

//对事件进行一下整合。方便后续使用
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

using func_t = function<void(shared_ptr<Connection>)>; // 定义一个函数对象
const int num = 100;
const int g_buffer_size = 128;

class Connection
{
public:
    Connection(int sock, shared_ptr<TcpServer> tcp_server_ptr)
        : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
    {
    }
    void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb) // 设置回调函数
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    int SockFd()
    {
        return _sock;
    } 

    void AppendInbuffer(const string& info)
    {
        _inbuffer += info;
    }

    void AppendOutbuffer(const string& info)
    {
        _outbuffer += info;
    }

    string& Inbuffer()
    {
        return _inbuffer;
    }

    string& Outbuffer()
    {
        return _outbuffer;
    }

    

    ~Connection()
    {
    }

private:
    int _sock;         // 链接所在的套接字
    string _inbuffer;  // string二进制流，vector, 读到的数据放到这里。
    string _outbuffer; // 未来通过套接字读取数据时，从inbuffer读取。

public:
    func_t _recv_cb;   // 读回调。 一旦数据就绪，就使用这个函数。
    func_t _send_cb;   // 写回调。
    func_t _except_cb; //

    // 添加一个指向底层TcpServer对象的回指指针。
    shared_ptr<TcpServer> _tcp_server_ptr;
    string _ip;
    uint16_t _port;
};

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port), _epoller_ptr(new Epoller()), 
        _listensock_ptr(new Socket()), _quit(false),
        _OnMessage(OnMessage),
        _self(shared_ptr<TcpServer>(this))
    {
    }
    ~TcpServer()
    {
        cout << "~  TcpServer()"<< endl;
    }

    void Init()
    {
        _listensock_ptr->InitSocket();
        SetNonBlockOrDie(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();

        AddConnection(_listensock_ptr->Fd(), EVENT_IN, 
        bind(&TcpServer::Accepter, this, placeholders::_1)
        , nullptr, nullptr, "0.0.0.0", _port); // 监听套接字没有回调函数， 他调用的是连接管理器。
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, 
        func_t except_cb, const string& ip, uint16_t port)
    {
        // 还要给sock也创建一个connection  对象，将sock添加到connection中，同时sock和connection放入connections。
        shared_ptr<Connection> new_connection = make_shared<Connection>(sock, _self); // 这里是构造connection。
        new_connection->SetCallBack(recv_cb, send_cb, except_cb);                  //构造shared_ptr， 构造自己的。但是不应该构造，应该传拷贝
        new_connection->_ip = ip;
        new_connection->_port = port;
        _Connections.insert(make_pair(sock, new_connection)); // 将sock套接字和conntection放到映射表中。


        // 我们添加对应的事件，除了要加到内核中，关注fd， event
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event); // 将对应sock套接字添加到内核中只是第一步

        lg(Debug, "add a new connection success, sockfd is : %d", sock);
    }

    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollErWait(revs, num, timeout); // 获得n个就绪事件

        for (int i = 0; i < n; i++)
        {
            // 事件就绪处理
            uint32_t event = revs[i].events;
            int sock = revs[i].data.fd;
            //如果是EPOLLERR和EPOLLHUP， 也就是事件出错了， 异常了。统一把所有的事件异常转化为读写问题。
            //这样就能让异常集中处理， 不再扩散到代码的任意位置。
            if (event & EPOLLERR) 
                event |= (EPOLLIN | EPOLLOUT);
            if (event & EPOLLHUP)
                event |= (EPOLLIN | EPOLLHUP);

            //事件派发里， 只需要处理EPOLLIN和EPOLLOUT就可以了。因为出异常只可能出现在这两个时间里面。
            if ((event & EPOLLIN) && IsConnectionSafe(sock))
            {
                if (_Connections[sock]->_recv_cb)  //处理写事件
                {
                    _Connections[sock]->_recv_cb(_Connections[sock]);   //这里传过去的就是就绪的文件描述符所在的连接
                }
            }
            if ((event & EPOLLOUT) && IsConnectionSafe(sock))  //写事件被设置成关心， 去调用Sender方法
            {
                if (_Connections[sock]->_send_cb)
                {
                    _Connections[sock]->_send_cb(_Connections[sock]);
                }
            }
        }
    }

    void Accepter(shared_ptr<Connection> connection)
    {
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            int sock = ::accept(connection->SockFd(), (sockaddr*)&peer, &len);
            if (sock > 0) //来到新连接
            {
                uint16_t peerport = ntohs(peer.sin_port);      //网络序列转主机序列
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                string peerip = ipbuf;
                lg(Debug, "get a new client, get info-> [%s: %d], sockfd : %d\n", ipbuf, peerport, sock);

                SetNonBlockOrDie(sock); //设置非阻塞
                //listensock只需要recv_rb， 但是其他的sock， 读， 写， 处理异常都要有
                AddConnection(sock, EVENT_IN, bind(&TcpServer::Recver, this, placeholders::_1),
                bind(&TcpServer::Sender, this, placeholders::_1),
                bind(&TcpServer::Excepter, this, placeholders::_1), peerip, peerport);   //添加新连接事件
            }
            else
            {
                if (errno == EWOULDBLOCK)  break; //底层没有数据
                else if (errno == EINTR) continue;   //EINTR为信号中断，信号导致的读取出错，挂起
                else break;
            }
        }
    }

    //事件管理器
    //读数据应不应该关心数据的格式？不应该。服务器只需要关心IO数据就可以， 有没有读完， 报文格式细节， 你不用管
    void Recver(shared_ptr<Connection> connection)  //这里传过来的
    {
        //
        int sock = connection->SockFd(); //得到文件描述符

        //ET模式， 必须把数据全部读完
        while (true)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);  //设置为0，原fd为非阻塞读取， 所以这里就是非阻塞读取。
            if (n > 0)    //读取成功
            {
               connection->AppendInbuffer(buffer); //读取成功， 就不断的将buffer缓冲区里面的数据放到connection里面维护的inbuffer里面。 
            }
            else if (n == 0)  //对端把连接关了
            {
                lg(Info, "sockfd: %d, client info %s : %d quit...", connection->SockFd(), connection->_ip.c_str(), connection->_port);  
                //一旦对方连接关了， 我们对应的这个连接， 就直接进入异常处理的流程:
                connection->_except_cb(connection); 
                return;
            }
            else  //读取出错
            {
                //非阻塞处理的时候：出错了errno == EWOULDBLACK， 表明本轮数据已经读完。那么文件描述符没有问题。
                //如果errno == EINET， 表明是因为被某个信号中断了，文件描述符没有问题， 并且数据可能没有读完， 继续读取.
                //如果errno == 其他的， 那么就是说明读取出错了， 文件描述符出问题了， 这个连接就可以Excepter了。
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg(Waring, "sockfd: %d, client info %s : %d recv error...", connection->_ip.c_str(), connection->_port);  
                    connection->_except_cb(connection);
                    return;
                }
                
            }
        }

        //数据有了， 但不一定全。1、先检测。2、如果有完整报文， 就处理。
        _OnMessage(connection);   //读到的所有的数据， 都在connection内部。读完之后回调给上层。
    }
    void Sender(shared_ptr<Connection> connection)
    {
        auto& outbuffer = connection->Outbuffer();
        while (true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0); 
            if (n > 0)
            {
                outbuffer.erase(0, n);
                if (outbuffer.empty()) break;

            }
            else if(n == 0)
            {
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else 
                {
                    lg(Waring, "sockfd: %d, client info %s : %d send error...", connection->_ip.c_str(), connection->_port);  
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        if (!outbuffer.empty())
        {
            //开启对写事件的关心
            EnableEvent(connection->SockFd(), true, true);
        }
        else
        {
            //关闭对写事件的关心
            EnableEvent(connection->SockFd(), true, false);
        }

    }
    void Excepter(shared_ptr<Connection> connection)
    {
        int sockfd = connection->SockFd();
        lg(Debug, "Excepter hander sockfd : %d, client info %s : %d Excepter hander...",
            sockfd, connection->_ip.c_str(), connection->_port);  
        //异常处理
        //1、先移除关心事件
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, sockfd, 0);
        //2、关闭异常的文件描述符
        close(sockfd);
        
        //3、将fd->connection映射表中将对应的连接进行移除。即从unordered_map中移除
        _Connections.erase(sockfd); //erase释放， 就是释放整个节点， 这个节点是{sockfd, connection_ptr} //释放对应的节点。但是
        //出现了双重释放。 双重释放了什么？ 
        lg(Debug, "remove %d from, _connections...\n", connection->SockFd()); 
    }

    bool IsConnectionSafe(int fd)   //判断连接是否安全
    {
        auto iter = _Connections.find(fd);
        if (iter == _Connections.end())
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | (EPOLLET));

        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, events);
    }

    void Loop()
    {
        // _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, _listensock_ptr->Fd(), EVENT_IN);
        _quit = false;

        // 我们添加对应的事件， 除了要加到内核中， fd， event。
        // 也要给listensock也建立一个connection对象， 将listensock添加到connection中， 同时， listensock和connection放入_connections

        while (!_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        }

        _quit = true;
    }

    void PrintConnection()
    {
        cout << "_connections fd list: ";
        for (auto& connection : _Connections)
        {
            cout << connection.second->SockFd() << ", ";
            cout << "inbuffer : " << connection.second->Inbuffer() << "  ";
        }
        cout << endl;
    }

private:
    shared_ptr<Epoller> _epoller_ptr;   // epoll模型
    shared_ptr<Socket> _listensock_ptr; // 套接字， 用来监听新连接

    unordered_map<int, shared_ptr<Connection>> _Connections; // 从一个文件描述符到一个epoller链接的映射。服务器管理的所有的链接
    struct epoll_event revs[num];
    uint16_t _port;
    bool _quit;
    shared_ptr<TcpServer> _self;

    func_t _OnMessage;  //让上层处理信息，将数据交给上层
};

#endif /* D4F97547_84E3_4137_B95D_E2179C0CD3ED */
